Reference
Note, that you need to import this library to any context expect Global
Needs["AnimationFramework`"->"af`"] // Quiet;
Scene
af`Scene[opts___] _Scene
constructs the main object Scene
, where animation is going to be rendered. It also serves the purpose of a hosting object for primitives, animations, workers and etc.
The local coordinates are limited to for the square aspect ratio, otherwise the smallest side takes is scaled to 1:
Scenes can be reused multiple times in different animations.
Options
ImageSize
By the default is Medium
. Not that unlike in Graphics
here the size is pixel perfect.
"TimeMarkers"
Sets the time markers from the Timeline
Other options
The rest is inherited from Graphics. PlotRange
, ImageSizeRaw
, cannot be changed.
Animation Flow
All animations on the scene should follows the pattern:
- append objects
- animate
- remove objects
By design most of methods and functions of AF framework return Promise expression. In this regard, it makes sense to describe your animation flow using Asynchronous Functions:
AsyncFunction[scene, Module[{},
...
]][scene];
Supported methods
AddTo
The most important method for the animation scene or to a layer:
af`AddTo[_Scene | layer_, primitives_] _af`Private`entity
af`AddTo[_Scene | layer_, primitives_, {
prop1_String -> value1_,
...
}] _af`Private`entity
where primitives
can be any 2D graphics object, or an expression, that produces graphics objects or your custom written WLJS Function that support .update
method.
Mutable properties are embed using Slot
expression aka
Primitive[#prop1, #prop2] ...
Basic example:
disk = af`AddTo[scene, {Red, Disk[{0,0}, 1]}];
However, it does not have any mutable properties. Here is another example
disk = af`AddTo[scene, {Red, Disk[#pos, #r]}, {
"pos" -> {0.,0.},
"r" -> 0.5
}];
On the created entity the following methods are supported
Supported methods
Update
Assigns a new value to a given property or list of properties
af`Update[scene_Scene, entity_, prop_String -> newValue_ ..]
af`Update[scene_Scene, entity_, {
prop1_String -> val1_,
...
}]
af`Update[entity_, {__Rule}]
The action will be applied immediately without easing. For example:
disk = af`AddTo[scene, {Red, Disk[#pos, #r]}, {
"pos" -> {0.,0.},
"r" -> 0.5
}];
af`Update[scene, disk, "pos"->RandomReal[{-1,1}, 2]];
Remove
Removes a single object or object from the scene or in a layer (including all children)
af`Remove[_af`Private`entity | _af`Private`runner | _af`Private`worker]
af`Remove[layer_af`Private`entity]
af`Remove[_Scene]
Remove
acting on Scene
does not remove the scene itself, but it disposes all objects added to it including the nested ones (layers). Use it as a fast way to clean up the scene.
Animate
It animates the transition of a property or set of properties with a given easing function on the scene
af`Animate[scene_Scene, entity_, prop_String -> value_, easing_:"CubicInOut", duration_:1] _Promise
af`Animate[scene_Scene, entity_, {
prop1_String -> value1_,
prop2_String -> value2_, ...
}, easing_:"CubicInOut", duration_:1] _Promise
where easing
is function used for animating the transition:
"Linear"
"CubicInOut"
or"Ease"
"QuadIn"
"QuadOut"
"LinearEuclidean"
(usually used for colors)Function[{t, initial, current, target}, ...]
and duration
is given in seconds.
In addition Animate
can be applied to any held _Symbol
:
af`Animate[scene_Scene, Hold[symbol_], newValue_, easing_:"CubicInOut", duration_:1] _Promise
or a Layer entity
af`Animate[scene_Scene, layer_, prop_String->newValue_, easing_:"CubicInOut", duration_:1] _Promise
Animate
always returns Promise, which is resolved when the animation is finished, i.e.:
wait for the animation to be finished
af`Animate[scene, object, "prop"->1.0] // Await;
wait for multiple animations to be finished
{
af`Animate[scene, object1, "prop"->1.0],
af`Animate[scene, object2, "prop"->1.0],
}// Await;
For animating multiple properties of the same object - use
af`Animate[scene, object1, {"prop1"->1.0, "prop2"->3.0}]
Animation is done fully using Wolfram Kernel (no Javascript easing is involved) at the maximum available frame rate (dynamic).
For example:
scene = af`Scene[];
scene
AsyncFunction[scene, Module[{d},
d = af`AddTo[scene, {
Opacity[#o],
Translate[
Rotate[
Rectangle[{-0.5,-0.1}, {0.5,0.1}]
, #r]
, #c]
}, {
"o" -> 0.,
"r" -> 0.,
"c" -> {0,0}
}];
af`Animate[scene, d, {"o" -> 1.0,"r" -> 3.14}, "Ease", 1.0] // Await;
PauseAsync[1] // Await;
af`Animate[scene, d, {"o"->0., "r"->0}, "Ease", 1.0] // Await;
af`Remove[d];
]][scene];
Supported property types
Real
,Integer
List
of integers, realsList
ofList
and etcString
with numbers inside
In principle anything, which can be combined with multiplication and sum can work as a property value.
Strings
String interpolation is done in steps:
- exploding string into string and number-like string
- convert number-like strings into array
- interpolate a array (using easing function) between the original and target value (transformed from a string)
- compose back into a string
For example, one can easily animate the following SVG attribute
scene = af`Scene[];
scene
AsyncFunction[scene, Module[{layer},
layer = af`Layer[scene, {
SVGAttribute[#children, "style"->#style]
}, {"style" -> "filter: blur(0px)"}];
af`AddTo[layer, Table[{RandomColor[], Disk[RandomReal[{-1,1},2], 1.0]}, {10}]];
af`Animate[scene, layer, "style"->"filter: blur(100px)", "Ease", 5]//Await;
af`Remove[layer];
]][scene];
Layer
It creates an isolated group of objects (layer):
af`Layer[scene_Scene | layer_, body_] _af`Private`entity
af`Layer[scene_Scene | layer_, body_, {
prop1_String -> value1_,
...
}] _af`Private`entity
where body
is defined similar to one in AddTo:
{
Opacity[0.5],
#children
}
or
{
Opacity[#opacity],
Translate[#children, #coordinates]
}
where #children
is a special reserved slot to specify where to append children. You can append objects similar to Scene
using AddTo expression:
layer = af`Layer[scene, {
Opacity[#opacity],
Translate[#children, #coordinates]
}, {"opacity" -> 0.5, "coordinates"->{0,0}}];
af`AddTo[layer, Table[{RandomColor[], Disk[RandomReal[{-0.2,0.2},2], 0.05]}, {10}]];
Z-ordering
To preserve z-order of your primitives, one can use Layer
together with SVGGroup.
Supported methods
Loop
It create custom animation loop, which is repeated until manually removed
af`Loop[scene_Scene, entity_, property_String, function_, duration_:1] _af`Private`runner
where function
should have the following form:
Function[{t, prevValue, cycleNumber},
]
Here t
goes from 0
to 1
in duration
seconds, and cycleNumber
represents the number of rounds passed.
function
is fired every frame
How to stop a loop
Before removing a loop animation call Await
Finish on the loop object to wait until the cycle is fully finished. Finish
returns promise, which is resolved on the end of the current cycle. This will help to avoid abrupt stop of the loop. For example
scene = af`Scene[];
scene
stop = InputButton["Stop"]
AsyncFunction[scene, Module[{d, loop},
d = af`AddTo[scene, {
Rotate[
Rectangle[{-0.5,-0.1}, {0.5,0.1}]
, #r]
}, {
"r" -> 0.
}];
loop = af`Loop[scene, d, "r", Function[{t, o, n},
3.14 t + (n-1) 3.14
], 2];
stop // Await;
af`Finish[loop] // Await;
af`Remove[loop];
PauseAsync[0.5] // Await;
af`Remove[d];
]][scene]
Supported methods
Worker
Workers are similar to Loops, but allow to execute an arbitrary code concurrently with the main animation on each frame:
af`Worker[scene_Scene, function_] _af`Private`worker
where function
has the following form
Function[absoluteTime, ...]
You can place an entire new animation sequence to a worker as Asynchronous Functions:
AsyncFunction[absoluteTime, ...]
function
is called every frame, but only after the execution of the previous function
call is finished. absoluteTime
is a time in seconds passed after the worker has been created.
For example:
scene = af`Scene[];
scene
stop = InputButton["Stop"]
AsyncFunction[scene, Module[{d, worker},
d = af`AddTo[scene, {
Rotate[
Rectangle[{-0.5,-0.1}, {0.5,0.1}]
, #r]
}, {
"r" -> 0.
}];
worker = af`Worker[scene, AsyncFunction[Null,
af`Animate[scene, d, "r"->Pi/2] // Await;
af`Animate[scene, d, "r"->-Pi/2] // Await;
]];
stop // Await;
af`Finish[worker] // Await;
af`Remove[worker];
PauseAsync[0.5] // Await;
af`Remove[d];
]][scene]
How to stop a worker
Before removing a worker call Await
Finish on the worker object to wait until the execution is fully finished (as on the example above). Finish
returns promise, which is resolved on the end of the current cycle. This will help to avoid abrupt stop.
Supported methods
Finish
af`Finish[worker_ | loop_ | _Promise] _Promise
It is a special promise generator used for waiting for various events:
Any Promise
object will be returned without changes. It only makes sense to use it with Await
expression:
af`Finish[worker1] // Await
or multiple events
{
af`Finish[worker1],
af`Finish[worker2]
} // Await
RecordAnimation
Read more at Recording page
To render the animation into the sequence of images:
af`RecordAnimation[animationFunction_, opts___] _af`Private`recorder
where animationFunction
is your async animation function, that accepts Scene
object and opts
are the same as for Scene including extra:
FrameRate
- frames per second (60
by the default)GeneratedAssetLocation
- by the default is$TemporalDirectory
GeneratedAssetFormat
- format for rendered sequence:"PNG"
,"JPEG"
CompressionLevel
- by the default is0.2
(applicable only for JPEG)"TimeMarkers"
- sets the time markers from the Timeline
Properties
recorder
object provides the following fields:
"GeneratedAssetLocation"
- path to sequence"Recording"
- status
TimelinedAnimation
Read more at Timeline page
To play an animation with a graphical timeline attached:
af`RecordAnimation[animationFunction_, opts___] _af`Private`timelined
where animationFunction
is your async animation function, that accepts Scene
object and opts
are the same as for Scene including extra:
"TimeMarkers"
- sets the time markers from the Timeline"AudioClips"
- restores recorded voice overs
Note, RecordAnimation
has HoldFirst
attribute.
Properties
timelined
object provides the following fields:
"TimeMarkers"
- current time markers"AudioClips"
- gets audio clips from the timeline"AudioClipsAsync"
- gets audio clips from the timeline
Utilities
Color
af`Color[_Hue | _RGBColor | _LABColor] _List
a color conversion tool, which turns any color representation to a list of RGB values. This comes handy with RGBColor, since to update it you need to provide a list instead of an actual color:
{RGBColor[#color], ...}
then when you update
af`Animate[... "color"->af`Color[Red], ...]
PauseAsync
A modified version of system default PauseAsync
with extra argument
PauseAsync[scene_Scene, n_Integer | n_Real] _Promise
Here the time delay will be in sync with Scene. It makes sense during the Recording process, where the actual time is fixed with a frame rate.
RecorderToVideo
It converts recorder
object to Video after the rendering is finished
af`RecorderToVideo[_af`Private`recorder] _Video
Marker
A helper to wait or extract property from Timeline markers
wait for the marker
af`Marker[scene_Scene, name_String] _Promise
af`Marker[scene_Scene, name_String, "Start"] _Promise
wait for the end of the marker
af`Marker[scene_Scene, name_String, "End"] _Promise
take duration in seconds
af`Marker[scene_Scene, name_String, "Duration"] _Real
AudioFromClips
A helper function to convert a list of audio clips (voice overs) retrieved from the properties of timelined
object to a single audio object:
af`AudioFromClips[clips_List]
For example:
af`AudioFromClips[t["AudioClips"]]
where t
is your TimelinedAnimation object